﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;

namespace SpriteSheetCreator
{
    public partial class SpriteSheetCreatorForm : Form
    {
        protected string OutputFile = string.Empty;
        protected int MaxWidth = 1024;
        protected int MaxHeight = 1024;
        protected SpriteSheetMode SpriteSheetMode;
        protected int BorderWidth = 2;
        protected bool AutoSizeCalc = true;
        protected MaxRectsBinPack.FreeRectChoiceHeuristic FreeRectChoiceHeuristic = MaxRectsBinPack.FreeRectChoiceHeuristic.RectBestAreaFit;


        protected int BiggestItemSize = 0;
        protected int NumColumns = 0;

        protected Bitmap OutputImage;

        protected List<SourceImageData> ImageList = new List<SourceImageData>(); 

        public SpriteSheetCreatorForm()
        {
            InitializeComponent();
        }

        private void btnPickOutputImage_Click(object sender, EventArgs e)
        {
            saveFileDialog.InitialDirectory = Environment.CurrentDirectory;

            var dialogResult = saveFileDialog.ShowDialog();
            if (dialogResult == DialogResult.OK)
            {
                lblOutputFile.Text = saveFileDialog.FileName;

                OutputFile = saveFileDialog.FileName;
            }
        }

        private void btnClearImages_Click(object sender, EventArgs e)
        {
            listView.Clear();
        }

        private void btnAddImages_Click(object sender, EventArgs e)
        {
            listView.Clear();
            imageList1.Images.Clear();
            ImageList.Clear();

            openFileDialog.InitialDirectory = Environment.CurrentDirectory;

            var dialogResult = openFileDialog.ShowDialog();
            if (dialogResult == DialogResult.OK)
            {
                foreach (var fileName in openFileDialog.FileNames)
                {
                    var fileNameNice = Path.GetFileName(fileName);



                    var image = (Bitmap)Image.FromFile(fileName);
                    ImageList.Add(new SourceImageData()
                    {
                        Path = fileName,
                        Image = image
                    });

                    imageList1.Images.Add(fileNameNice, image);
                    listView.Items.Add(fileName, fileNameNice, fileNameNice);

                }
            }
        }

        private void btnBuild_Click(object sender, EventArgs e)
        {
            if (String.IsNullOrEmpty(OutputFile))
                MessageBox.Show("Please select an output file");
            else if (listView.Items.Count == 0)
                MessageBox.Show("Please add at least one image");
            else
            {
                MaxHeight = (int)numMaxHeight.Value;
                MaxWidth = (int)numMaxWidth.Value;
                AutoSizeCalc = chkSizeAuto.Checked;
                BorderWidth = (int) numBorderWidth.Value;

                if (radFitModePowerOfTwo.Checked)
                    SpriteSheetMode = SpriteSheetMode.PowerOfTwo;
                else
                    SpriteSheetMode = SpriteSheetMode.MaxRects;

                progressBar.Value = 0;

                lblStatus.Text = "Processing...";

                // Calc size
                if (AutoSizeCalc)
                {
                    foreach (var sourceImageData in ImageList)
                    {
                        var image = sourceImageData.Image;

                        if (sourceImageData.Image.Width > BiggestItemSize)
                            BiggestItemSize = image.Width;
                        else if (image.Height > BiggestItemSize)
                            BiggestItemSize = image.Height;
                    }

                    numSize.Value = BiggestItemSize;
                }



                int numItems = ImageList.Count;

                if (SpriteSheetMode == SpriteSheetMode.PowerOfTwo)
                {
                    // Find power of two
                    NumColumns = 1;
                    while (Math.Pow(NumColumns, 2) < numItems)
                        NumColumns++;

                    // Calc the image size
                    int fullSize = ((NumColumns + 1) * BorderWidth) + (NumColumns * BiggestItemSize);

                    OutputImage = new Bitmap(fullSize, fullSize);

                    for (int x = 0; x < fullSize; x++)
                    {
                        for (int y = 0; y < fullSize; y++)
                        {
                            OutputImage.SetPixel(x, y, Color.Transparent);
                        }
                    }

                    // Go through each image and iterate through columns, then rows
                    int xCount = 0;
                    int yCount = 0;

                    int index = 0;

                    foreach (var sourceImageData in ImageList)
                    {
                        int xPos = (xCount*(BiggestItemSize + BorderWidth)) + BorderWidth;
                        int yPos = (yCount*(BiggestItemSize + BorderWidth)) + BorderWidth;

                        // Go through each pixel in the source and add to our output
                        var image = sourceImageData.Image;
                        for (int x = 0; x < image.Width; x++)
                        {
                            for (int y = 0; y < image.Height; y++)
                            {
                                var pixelColor = image.GetPixel(x, y);

                                OutputImage.SetPixel(x + xPos, y + yPos, pixelColor);
                            }
                        }



                        // Update progress bar
                        float percent = index/(float) numItems;
                        progressBar.Value = (int)(percent*100);


                        xCount++;
                        if (xCount >= NumColumns)
                        {
                            xCount = 0;
                            yCount++;
                        }
                        index++;
                    }


                }
                else if (SpriteSheetMode == SpriteSheetMode.MaxRects)
                {

                    OutputImage = new Bitmap(MaxWidth, MaxHeight);
                    for (int x = 0; x < MaxWidth; x++)
                    {
                        for (int y = 0; y < MaxHeight; y++)
                        {
                            OutputImage.SetPixel(x, y, Color.Transparent);
                        }
                    }

                    var maxRectsBinPack = new MaxRectsBinPack(MaxWidth, MaxHeight, false);

                    int index = 0;
                    int doubleBorderWidth = BorderWidth*2;

                    foreach (var sourceImageData in ImageList)
                    {
                        // Go through each pixel in the source and add to our output
                        var image = sourceImageData.Image;

                        var rect = maxRectsBinPack.Insert(image.Width + doubleBorderWidth, image.Height + doubleBorderWidth, FreeRectChoiceHeuristic);

                        for (int x = 0; x < image.Width; x++)
                        {
                            for (int y = 0; y < image.Height; y++)
                            {
                                var pixelColor = image.GetPixel(x, y);

                                int posX = BorderWidth + (int) rect.Left + x;
                                int posY = BorderWidth + (int) rect.Top + y;

                                OutputImage.SetPixel(posX, posY, pixelColor);
                            }
                        }

                        // Update progress bar
                        float percent = index / (float)numItems;
                        progressBar.Value = (int)(percent * 100);


                        index++;
                    }




                }


                OutputImage.Save(OutputFile);
                progressBar.Value = progressBar.Maximum;
                lblStatus.Text = "Completed.";
                pictureBox.ImageLocation = OutputFile;
            }
        }

        private void chkSizeAuto_CheckedChanged(object sender, EventArgs e)
        {
            numSize.Enabled = !chkSizeAuto.Checked;
        }

        private void radFitModeMaxRects_CheckedChanged(object sender, EventArgs e)
        {
            UpdateControlsEnabled();
        }


        public void UpdateControlsEnabled()
        {
            MaxHeight = (int)numMaxHeight.Value;
            MaxWidth = (int)numMaxWidth.Value;
            AutoSizeCalc = chkSizeAuto.Checked;
            BorderWidth = (int)numBorderWidth.Value;

            if (radFitModePowerOfTwo.Checked)
                SpriteSheetMode = SpriteSheetMode.PowerOfTwo;
            else
                SpriteSheetMode = SpriteSheetMode.MaxRects;


            if (SpriteSheetMode == SpriteSheetMode.PowerOfTwo)
            {
                numSize.Enabled = false;
                chkSizeAuto.Checked = true;
                chkSizeAuto.Enabled = true;
                numMaxWidth.Enabled = false;
                numMaxHeight.Enabled = false;
                cmbMaxRectsMethod.Enabled = false;
            }
            else if (SpriteSheetMode == SpriteSheetMode.MaxRects)
            {
                numSize.Enabled = false;
                chkSizeAuto.Checked = true;
                chkSizeAuto.Enabled = false;
                numMaxWidth.Enabled = true;
                numMaxHeight.Enabled = true;
                cmbMaxRectsMethod.Enabled = true;
            }
        }

        private void radFitModePowerOfTwo_CheckedChanged(object sender, EventArgs e)
        {
            UpdateControlsEnabled();
        }

        private void cmbMaxRectsMethod_SelectedIndexChanged(object sender, EventArgs e)
        {
            FreeRectChoiceHeuristic = (MaxRectsBinPack.FreeRectChoiceHeuristic) cmbMaxRectsMethod.SelectedIndex;
        }

        private void SpriteSheetCreatorForm_Load(object sender, EventArgs e)
        {
            cmbMaxRectsMethod.Items.Clear();

            cmbMaxRectsMethod.DataSource = Enum.GetNames(typeof(MaxRectsBinPack.FreeRectChoiceHeuristic));

            cmbMaxRectsMethod.SelectedIndex = 3; // BottomLeft rule
        }

        private void btnQuickSize128_Click(object sender, EventArgs e)
        {
            QuickSetSize(128);
        }

        private void QuickSetSize(int size)
        {
            MaxHeight = MaxWidth = size;
            numMaxHeight.Value = MaxHeight;
            numMaxWidth.Value = MaxWidth;
        }

        private void btnQuickSize256_Click(object sender, EventArgs e)
        {
            QuickSetSize(256);
        }

        private void btnQuickSize512_Click(object sender, EventArgs e)
        {
            QuickSetSize(512);
        }

        private void btnQuickSize1024_Click(object sender, EventArgs e)
        {
            QuickSetSize(1024);
        }

        private void btnQuickSize2048_Click(object sender, EventArgs e)
        {
            QuickSetSize(2048);
        }

        private void btnQuickSize4096_Click(object sender, EventArgs e)
        {
            QuickSetSize(4096);
        }
    }
}
